package fr.teuteuf.runstickmanrun.screen;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.controllers.Controller;
import com.badlogic.gdx.controllers.mappings.Ouya;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.EdgeShape;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.World;

import fr.teuteuf.runstickmanrun.AbstractKeyMap;
import fr.teuteuf.runstickmanrun.Constants;
import fr.teuteuf.runstickmanrun.IRenderable;
import fr.teuteuf.runstickmanrun.IUpdatable;
import fr.teuteuf.runstickmanrun.RunStickmanRun;
import fr.teuteuf.runstickmanrun.entity.AbstractEntity;
import fr.teuteuf.runstickmanrun.entity.BigLeftMonster;
import fr.teuteuf.runstickmanrun.entity.Player;
import fr.teuteuf.runstickmanrun.map.tile.IDamagingTile;
import fr.teuteuf.runstickmanrun.map.tile.SolidBoxTile;
import fr.teuteuf.runstickmanrun.map.tile.Tile;

public class SinglePlayerScreen extends AbstractGameScreen {
	
	private Box2DDebugRenderer debugRenderer;
	
	private OrthographicCamera gameCamera;
	private SpriteBatch gameBatch;
	private OrthographicCamera interfaceCamera;
	private SpriteBatch interfaceBatch;
	private Texture background;
	private float backgroundPosition;
	
	private Player player;
	private BigLeftMonster bigLeftMonster;
	
	private Body bottomBorder;
	private int score;
	private int bestDistance;
	private int distanceMonster;
	
	private AbstractKeyMap keyMap;
	
	public SinglePlayerScreen(RunStickmanRun runStickmanRun) {
		this.keyMap = runStickmanRun.getKeyMap();
		this.runStickmanRun = runStickmanRun;
	}

	@Override
	public void create() {
		super.create();
		Tile.loadTexture();
		Player.loadTexture();
		BigLeftMonster.loadTexture();
		background = new Texture(Gdx.files.internal("backgrounds/rocks.png"));
		reinit();
	}

	@Override
	public void reinit() {
		super.reinit();
		score = 0;
		bestDistance = 0;
		backgroundPosition = 0;
		
		gameCamera = new OrthographicCamera();
		gameCamera.setToOrtho(false, Constants.WIDTH_RESOLUTION, Constants.HEIGHT_RESOLUTION);
		gameBatch = new SpriteBatch();
		interfaceCamera = new OrthographicCamera();
		interfaceCamera.setToOrtho(false, Constants.WIDTH_RESOLUTION, Constants.HEIGHT_RESOLUTION);
		interfaceBatch = new SpriteBatch();
		
		player = new Player(world, 7 * Tile.TILE_SIZE, Tile.TILE_SIZE * 1, 0.5f, 2f, 0);
		updatables.add(player);
		
		
		
		bigLeftMonster = new BigLeftMonster(world, 0 * Tile.TILE_SIZE, Tile.TILE_SIZE * 1, player.getMovingSpeedMax());
		updatables.add(bigLeftMonster);
		renderables.add(bigLeftMonster);
		
		//Bottom border
		BodyDef botBorderbodyDef = new BodyDef();
		botBorderbodyDef.position.set(0, 0);
		EdgeShape botBorderShape = new EdgeShape();
		botBorderShape.set(0, 0, Constants.WIDTH_RESOLUTION * Constants.WORLD_TO_BOX, 0);
		FixtureDef botBorderFixtureDef = new FixtureDef();
		botBorderFixtureDef.shape = botBorderShape;
		botBorderFixtureDef.isSensor = true;
		bottomBorder = world.createBody(botBorderbodyDef);
		bottomBorder.createFixture(botBorderFixtureDef);
		bottomBorder.setUserData(this);
		botBorderShape.dispose();
		
		if(Constants.IS_DEBUG_MODE) {
			debugRenderer = new Box2DDebugRenderer();
		}
	}

	@Override
	public void update(float elapsedTime) {
		super.update(elapsedTime);
		world.step(1/60f, 6, 2);
		
		//Update game camera position
		float camPosition = gameCamera.position.x;
		if(player.getPosition().x * Constants.BOX_TO_WORLD > Constants.WIDTH_RESOLUTION / 2) {
			float newCamPosition = player.getPosition().x * Constants.BOX_TO_WORLD;
			gameCamera.position.set(newCamPosition, gameCamera.position.y, gameCamera.position.z);
			backgroundPosition = (backgroundPosition + (newCamPosition - camPosition) / 2) % background.getWidth();
		}
		
		//Update bottom border position
		bottomBorder.setTransform(player.getPosition().x - Constants.WIDTH_RESOLUTION * Constants.WORLD_TO_BOX / 2, 0, 0);
		
		gameCamera.update();
		interfaceCamera.update();
		
		for (IUpdatable updatable : updatables) {
			updatable.update(elapsedTime);
		}
		
		int distanceTemp = (int) (player.getPosition().x * Constants.BOX_TO_WORLD) / 100;
		if(distanceTemp > bestDistance) {
			score += (distanceTemp - bestDistance) * (getCurrentLevel() + 1);
			bestDistance = distanceTemp;
		}
		
		distanceMonster = (int) (player.getPosition().x - bigLeftMonster.getPosition().x);
		
		if(player.getHealth() <= 0) {
			this.gameover();
		}
	}

	@Override
	public void render() {
		Gdx.gl.glClearColor(0, 0, 0, 1);
		Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

		//Render game content
		gameBatch.setProjectionMatrix(gameCamera.combined);
		gameBatch.begin();
		//Render background
		//TODO
		float paintingPositionY = 0;
		while(paintingPositionY < Constants.HEIGHT_RESOLUTION * gameCamera.zoom) {
			float paintingPositionX = backgroundPosition - Constants.WIDTH_RESOLUTION;
			while(paintingPositionX < gameCamera.position.x + (Constants.WIDTH_RESOLUTION / 2 * gameCamera.zoom)) {
				if(paintingPositionX > gameCamera.position.x - (Constants.WIDTH_RESOLUTION / 2 * gameCamera.zoom) - background.getWidth()) {
					gameBatch.draw(background, paintingPositionX, paintingPositionY);
				}
				paintingPositionX += background.getWidth();
			}
			paintingPositionY += background.getHeight();
		}
		player.render(gameBatch);
		for (Tile tile : mapGenerator.getTiles()) {
			tile.render(gameBatch);
		}
		for (IRenderable renderable : renderables) {
			renderable.render(gameBatch);
		}
		gameBatch.end();
		
		//Render interface
		interfaceBatch.setProjectionMatrix(interfaceCamera.combined);
		interfaceBatch.begin();
		defaultFont.draw(interfaceBatch, "Score: "+score, 20, Constants.HEIGHT_RESOLUTION - 20);
		defaultFont.draw(interfaceBatch, "High Score: "+runStickmanRun.getHighScore(), 20, Constants.HEIGHT_RESOLUTION - 40);
		defaultFont.draw(interfaceBatch, "HP: "+(int) player.getHealth(), 20, Constants.HEIGHT_RESOLUTION - 60);
		defaultFont.draw(interfaceBatch, "Energy: "+(int) player.getEnergy(), 20, Constants.HEIGHT_RESOLUTION - 80);
		defaultFont.draw(interfaceBatch, "The MONSTER is "+distanceMonster+" meters away!", Constants.WIDTH_RESOLUTION/2 - 110, Constants.HEIGHT_RESOLUTION - 20);
		defaultFont.draw(interfaceBatch, "Level: "+getCurrentLevel(), Constants.WIDTH_RESOLUTION - 70, Constants.HEIGHT_RESOLUTION - 20);
		if(Constants.IS_DEBUG_MODE) {
			defaultFont.draw(interfaceBatch, "FPS: "+(int)(1/Gdx.graphics.getDeltaTime()), 20, 20);
			defaultFont.draw(interfaceBatch, "Bodies: "+world.getBodyCount(), 20, 40);
		}
		interfaceBatch.end();
		
		if(Constants.IS_DEBUG_MODE) {
			debugRenderer.render(world, gameCamera.combined.cpy().scl(Constants.BOX_TO_WORLD));
		}
	}
	
	private void gameover() {
		runStickmanRun.addScore((distanceMonster >= 0) ? score + distanceMonster : 0);
		runStickmanRun.changeCurrentScreen(RunStickmanRun.INDEX_SCORE_SCREEN, true);
	}

	@Override
	public void dispose() {
		gameBatch.dispose();
		interfaceBatch.dispose();
		Player.dispose();
		BigLeftMonster.dispose();
		background.dispose();
	}
	
	@Override
	public void beginContact(Contact contact) {
		Body bodyA = contact.getFixtureA().getBody(), bodyB = contact.getFixtureB().getBody();
		
		if(bodyA.getUserData() instanceof Player) {
			if((bodyB.getUserData() instanceof AbstractEntity || bodyB.getUserData() instanceof SolidBoxTile) && bodyA.getLinearVelocity().y < 0f) {
				player.setJumping(false);
				player.setDoubleJumping(false);
			}
		}
		
		if(bodyB.getUserData() instanceof Player) {
			if((bodyA.getUserData() instanceof AbstractEntity || bodyA.getUserData() instanceof SolidBoxTile)  && bodyB.getLinearVelocity().y < 0f) {
				player.setJumping(false);
				player.setDoubleJumping(false);
			}
		}

		if(bodyA.getUserData() instanceof Player || bodyB.getUserData() instanceof Player) {
			if(    bodyB.getUserData() instanceof SinglePlayerScreen || bodyA.getUserData() instanceof SinglePlayerScreen
				|| bodyB.getUserData() instanceof BigLeftMonster     || bodyA.getUserData() instanceof BigLeftMonster) {
					this.gameover();
			}
			
			if(bodyA.getUserData() instanceof IDamagingTile) {
				player.setHealth(player.getHealth() - ((IDamagingTile) bodyA.getUserData()).getDamage());
			} else if(bodyB.getUserData() instanceof IDamagingTile) {
				player.setHealth(player.getHealth() - ((IDamagingTile) bodyB.getUserData()).getDamage());
			}
		}
	}
	
	@Override
	public Player getFirstPlayer() {
		return player;
	}
	
	@Override
	public World getWorld() {
		return world;
	}

	@Override
	public boolean keyDown(int keycode) {
		handleKeyDown(keycode);
		return true;
	}

	@Override
	public boolean keyUp(int keycode) {
		handleKeyUp(keycode);
		return true;
	}

	@Override
	public boolean axisMoved(Controller arg0, int arg1, float arg2) {
		if(arg1 == Ouya.AXIS_LEFT_X) {
			if(arg2 > 0.4f) {
				handleKeyDown(keyMap.keyCodeRight);
				handleKeyUp(keyMap.keyCodeLeft);
			} else if(arg2 < -0.4f) {
				handleKeyDown(keyMap.keyCodeLeft);
				handleKeyUp(keyMap.keyCodeRight);
			} else {
				handleKeyUp(keyMap.keyCodeLeft);
				handleKeyUp(keyMap.keyCodeRight);
			}
		}
		return true;
	}

	@Override
	public boolean buttonDown(Controller arg0, int arg1) {
		handleKeyDown(arg1);
		return true;
	}

	@Override
	public boolean buttonUp(Controller arg0, int arg1) {
		handleKeyUp(arg1);
		return true;
	}

	private void handleKeyDown(int keyCode) {
		if(keyCode == keyMap.keyCodeLeft || keyCode == keyMap.keyCodeLeftBis) {
			player.moveLeft(true);
		} else if(keyCode == keyMap.keyCodeRight || keyCode == keyMap.keyCodeRightBis) {
			player.moveRight(true);
		} else if(keyCode == keyMap.keyCodeJump || keyCode == keyMap.keyCodeJumpBis) {
			player.jump(true);
		} else if(keyCode == keyMap.keyCodeSprint || keyCode == keyMap.keyCodeSprintBis) {
			player.sprint(true);
		}
	}
	
	private void handleKeyUp(int keyCode) {
		if(keyCode == keyMap.keyCodeLeft || keyCode == keyMap.keyCodeLeftBis) {
			player.moveLeft(false);
		} else if(keyCode == keyMap.keyCodeRight || keyCode == keyMap.keyCodeRightBis) {
			player.moveRight(false);
		} else if(keyCode == keyMap.keyCodeJump || keyCode == keyMap.keyCodeJumpBis) {
			player.jump(false);
		} else if(keyCode == keyMap.keyCodeSprint || keyCode == keyMap.keyCodeSprintBis) {
			player.sprint(false);
		}
	}
}
